package model;

import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.FieldDeclaration;
import japa.parser.ast.body.VariableDeclarator;
import japa.parser.ast.type.Type;
import japa.parser.ast.visitor.VoidVisitorAdapter;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is responsible for parsing a java file and creating a Java Object from it.
 * @author Corentin
 *
 */
public class ObjectLoader {
	private static final Logger logger = LoggerFactory
			.getLogger(ObjectLoader.class);

	private final String fileName;
	private CompilationUnit cu;
	private JavaObject parsedObject;
	
	/**
	 * Constructor.
	 * @param fileName
	 */
	public ObjectLoader(String fileName) {
		this.fileName = fileName;
	}

	public void parseObject() throws FileNotFoundException, ParseException {
		FileInputStream fis = new FileInputStream(fileName);
		cu = JavaParser.parse(fis);
		parsedObject = new JavaObject();
		
		ClassVisitor cv = new ClassVisitor();
		FieldsVisitor fv = new FieldsVisitor();
		cv.visit(this.cu, null);
		fv.visit(this.cu, null);
		parsedObject.setClassName(cv.getClassName());
		parsedObject.setFields(getFields(fv));
		
		try {
			fis.close();
		} catch (IOException e) {
			logger.warn("Error while closing the file...", e);
		}
	}
	
	public JavaObject getParsedObject() {
		return this.parsedObject;
	}

	private List<Attribute> getFields(FieldsVisitor fv) {
		if (this.cu != null) {
			List<Attribute> result = new ArrayList<Attribute>();
			for (int i = 0; i < fv.getVariables().size(); ++i) {
				// @formatter:off
				result.add(new Attribute(fv.getVariables().get(i).toString(),
						fv.getTypes().get(i).toString(), 
						fv.getModifiers().get(i)));
				// @formatter:on
			}
			return result;
		} else {
			return null;
		}
	}

	private static class ClassVisitor extends VoidVisitorAdapter<Object> {
		private String className = null;

		@Override
		public void visit(ClassOrInterfaceDeclaration n, Object arg) {
			className = n.getName();
		}

		public String getClassName() {
			return className;
		}
	}

	private static class FieldsVisitor extends VoidVisitorAdapter<Object> {
		private List<Integer> modifiers;
		private List<VariableDeclarator> variables;
		private List<Type> types;

		public FieldsVisitor() {
			this.modifiers = new ArrayList<Integer>();
			this.variables = new ArrayList<VariableDeclarator>();
			this.types = new ArrayList<Type>();
		}

		@Override
		public void visit(FieldDeclaration n, Object arg) {
			for (VariableDeclarator vd : n.getVariables()) {
				this.variables.add(vd);
				this.modifiers.add(n.getModifiers());
				this.types.add(n.getType());
			}
		}

		public List<Integer> getModifiers() {
			return this.modifiers;
		}

		public List<Type> getTypes() {
			return this.types;
		}

		public List<VariableDeclarator> getVariables() {
			return this.variables;
		}
	}
}
